using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;

namespace Pinwheel.LSerialize
{
    public enum CollectionType : byte
    {
        None,
        Array,
        List,
        Map,
    }
    
    public enum FieldType : byte
    {
        None,
        Number,
        String,
        Struct,
        Class,
    }

    [StructLayout(LayoutKind.Explicit, Size = 8)]
    public struct ValueHeader
    {
        [FieldOffset(0)]
        internal ushort Tag;
        [FieldOffset(2)]
        internal CollectionType CollectionType;
        [FieldOffset(3)]
        internal FieldType KeyType;
        [FieldOffset(4)]
        internal FieldType ValType;
    }
    
    [StructLayout(LayoutKind.Explicit)]
    public struct ReferenceHeader
    {
        [FieldOffset(0)] internal int Size;
        [FieldOffset(4)] internal ushort ValCount;
        [FieldOffset(6)] internal ushort RefCount;
        [FieldOffset(8)] internal long RefId;
    }
    
    [StructLayout(LayoutKind.Explicit, Size = 8)]
    public struct ValHeader
    {
        [FieldOffset(0)] public int Depth;
        [FieldOffset(4)] public int Tag;
    }

    [StructLayout(LayoutKind.Explicit, Size = 8)]
    public struct RefHeader
    {
        [FieldOffset(0)] public int InstId;
        [FieldOffset(4)] public int Size;
    }
    
    public static class LSerializeCode
    {
        public const int ValueTypeSize = 8;
        public const int ValueHeaderSize = 8;
        public const int ReferenceHeaderSize = 16;
    }

    public static class LSerializeHelper
    {
        public static FieldType GetFieldType(this Type type)
        {
            if (type.IsValueType)
            {
                return type.IsPrimitive ? FieldType.Number : FieldType.Struct;
            }
            else
            {
                return type == typeof(string) ? FieldType.String : FieldType.Class;
            }
            return FieldType.None;
        }

        public static CollectionType GetCollectionType(this Type type)
        {
            if (type.IsArray)
            {
                return CollectionType.Array;
            }
            else if (type.IsGenericType)
            {
                var genType = type.GetGenericTypeDefinition();
                if (genType == typeof(List<>))
                    return CollectionType.List;
                else if (genType == typeof(Dictionary<,>))
                    return CollectionType.Map;
            }
            return CollectionType.None;
        }
    }
}
